#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/time.h>
 
#include "data_type.h"
#include "alloc.h"
#include "memfunc.h"
#include "basefunc.h"
#include "struct_deal.h"
#include "crypto_func.h"
#include "memdb.h"
#include "message.h"
#include "ex_module.h"
#include "sys_func.h"

#include "whitelist_struct.h"
#include "whitelist_load.h"
// add para lib_include
struct policy_buffer
{
	int digest_num;
	int digest_size;
	BYTE * digest_array;
} * policy_buffer;

int  proc_whitelist_search(void * sub_proc,void *recv_msg);
int  proc_whitelist_load(void * sub_proc,void *recv_msg);
BYTE * read_whitelist(const char * filename,const int max_number, int size);

static int compare_hash(const void *a,const void *b)
{
	return memcmp(a,b,DIGEST_SIZE);
}
void * appbsearch(const void *key, const void *base, size_t nmemb, size_t size,
		int (*compar)(const void *, const void *));

struct policy_buffer * policy_add_package(struct policy_buffer * base_policy,struct policy_buffer * package_policy);
struct policy_buffer * policy_del_package(struct policy_buffer * base_policy,struct policy_buffer * package_policy);

int whitelist_load_init(void * sub_proc, void * para)
{
	int ret;
	// add yorself's module init func here
	proc_share_data_setpointer(NULL);
	policy_buffer=NULL;
	return 0;
}
int whitelist_load_start(void * sub_proc, void * para)
{
	int ret;
	void * recv_msg;
	int type;
	int subtype;
	// add yorself's module exec func here
	while(1)
	{
		usleep(time_val.tv_usec);
		ret=ex_module_recvmsg(sub_proc,&recv_msg);
		if(ret<0)
			continue;
		if(recv_msg==NULL)
			continue;
		type=message_get_type(recv_msg);
		subtype=message_get_subtype(recv_msg);
		if(!memdb_find_recordtype(type,subtype))
		{
			printf("message format (%d %d) is not registered!\n",
			message_get_type(recv_msg),message_get_subtype(recv_msg));
			continue;
		}
		if((type==TYPE(WHITELIST_SM3)) && (subtype==SUBTYPE(WHITELIST_SM3,BASE_WHITELIST)))
		{
			proc_whitelist_load(sub_proc,recv_msg);
		}
		else if((type==TYPE(WHITELIST_SM3)) && (subtype==SUBTYPE(WHITELIST_SM3,WHITELIST_POLICY)))
		{
			proc_whitelist_merge(sub_proc,recv_msg);
		}
		else if((type==TYPE(WHITELIST_SM3)) && (subtype==SUBTYPE(WHITELIST_SM3,FILE_DIGEST)))
		{
			proc_whitelist_search(sub_proc,recv_msg);
		}
	}
	return 0;
}

int proc_whitelist_load(void * sub_proc,void * recv_msg)
{
	
	int ret,fd;
	char uuid[DIGEST_SIZE*2];
	RECORD(WHITELIST_SM3,BASE_WHITELIST) * load_whitelist;
	void * new_msg;

	ret=message_get_record(recv_msg,&load_whitelist,0);
	if(ret<0)
		return ret;

	digest_to_uuid(load_whitelist->whitelist_uuid,uuid);
	uuid[DIGEST_SIZE*2]=0;
	policy_buffer=read_whitelist(uuid,MAX_WHITELIST_SIZE,DIGEST_SIZE);
	proc_share_data_setpointer(policy_buffer);

	new_msg=recv_msg;
        message_set_flag(new_msg,message_get_flag(new_msg) | MSG_FLAG_FOLLOW);
        ret=ex_module_sendmsg(sub_proc,new_msg);

	return 0;
}



int proc_whitelist_merge(void * sub_proc,void * recv_msg)
{
	
	int ret,fd;
    int i;
	char uuid[DIGEST_SIZE*2];
	RECORD(WHITELIST_SM3,WHITELIST_POLICY) * whitelist_policy;
	RECORD(WHITELIST_SM3,BASE_WHITELIST) * base_whitelist;
	RECORD(WHITELIST_SM3,PACKAGE_WHITELIST) * package_whitelist;
	void * new_msg;
    DB_RECORD *db_record;
    char * policy_path="policy";
    char list_name[DIGEST_SIZE*4];
    int path_len;

    struct policy_buffer * base_policy;
    struct policy_buffer * package_policy;
    struct policy_buffer * merge_policy;

	ret=message_get_record(recv_msg,&whitelist_policy,0);
	if(ret<0)
		return ret;
        
    db_record = memdb_find_byname(whitelist_policy->base_policy,TYPE_PAIR(WHITELIST_SM3,BASE_WHITELIST));
    if(db_record!=NULL)
        base_whitelist=db_record->record;
    else
	base_whitelist=NULL;
   

    Strcpy(list_name,policy_path);
    Strcat(list_name,"/");
    path_len=Strlen(list_name);

    int is_basepolicy_ok=0;
    if(base_whitelist!=NULL)
    {
	digest_to_uuid(base_whitelist->whitelist_uuid,list_name+path_len);
	list_name[path_len+DIGEST_SIZE*2]=0;
	base_policy=read_whitelist(list_name,MAX_WHITELIST_SIZE,DIGEST_SIZE);
	is_basepolicy_ok=1;
    }

    
    for(i=0;i<whitelist_policy->add_policy_num;i++)
    {

        db_record = memdb_find_byname(whitelist_policy->add_policys+64*i,TYPE_PAIR(WHITELIST_SM3,PACKAGE_WHITELIST));
        if(db_record!=NULL)
	{
             package_whitelist=db_record->record;
             digest_to_uuid(package_whitelist->whitelist_uuid,list_name+path_len);

	     package_policy=read_whitelist(list_name,MAX_WHITELIST_SIZE,DIGEST_SIZE);
	     if(is_basepolicy_ok)
        	base_policy= policy_add_package(base_policy,package_policy);
	     else
		base_policy = package_policy;
	}
    }

    for(i=0;i<whitelist_policy->del_policy_num;i++)
    {
        db_record = memdb_find_byname(whitelist_policy->del_policys+64*i,TYPE_PAIR(WHITELIST_SM3,PACKAGE_WHITELIST));
        if(db_record!=NULL)
	{	
             package_whitelist=db_record->record;
	     digest_to_uuid(package_whitelist->whitelist_uuid,list_name+path_len);

	    package_policy=read_whitelist(list_name,MAX_WHITELIST_SIZE,DIGEST_SIZE);
            base_policy= policy_del_package(base_policy,package_policy);
	}  
    }

	proc_share_data_setpointer(base_policy);
	char *temp_filename="whitelist_temp";
       fd=open(temp_filename,O_WRONLY|O_CREAT|O_TRUNC,0666);
	if(fd<0)
		return fd;
    for(i=0;i<base_policy->digest_num;i++)
    {
        ret=write(fd,base_policy->digest_array+i*base_policy->digest_size,base_policy->digest_size);
        if(ret<0)
            return -EINVAL;
    }
	close(fd);

	convert_uuidname(temp_filename,DIGEST_SIZE,whitelist_policy->whitelist_uuid,uuid);

	new_msg=message_create(TYPE_PAIR(WHITELIST_SM3,WHITELIST_POLICY),recv_msg);
    message_add_record(new_msg,whitelist_policy);
    ret=ex_module_sendmsg(sub_proc,new_msg);

	return 0;
}

int  proc_whitelist_search(void *sub_proc,void *recv_msg)
{
	int ret,fd;
	RECORD(WHITELIST_SM3,FILE_DIGEST) * file_digest;
	void * new_msg;
	struct policy_buffer * policys;

	ret=message_get_record(recv_msg,&file_digest,0);
	if(ret<0)
		return ret;

	policys=proc_share_data_getpointer();
	if(policys==NULL)
		return -EINVAL;

	if (appbsearch(file_digest->file_digest,policys->digest_array,policys->digest_num,DIGEST_SIZE, compare_hash))
	{
		file_digest->check_result=WHITELIST_CORRECT;
	}
	else
		file_digest->check_result=WHITELIST_MISMATCH;

	new_msg=message_create(TYPE_PAIR(WHITELIST_SM3,FILE_DIGEST),recv_msg);
	message_add_record(new_msg,file_digest);
	ex_module_sendmsg(sub_proc,new_msg);
	return 0;
}

BYTE * read_whitelist(const char * filename,const int max_number, int size)
{

	int ret=0,i = 0;
	int fd;

	unsigned int fsize;
	int number = 0;

        struct stat attribute;
	struct policy_buffer * policys;

	fd=open(filename,O_RDONLY);

	if(fd<0) {
		print_cubeerr("open policy file %s error!",filename);
		return NULL;
	}
        if(fstat(fd, &attribute)<0)
	{
                printf("fstat error\n");
		return NULL;
	}
        if(attribute.st_size<=0)
		return NULL;

	fsize = attribute.st_size;
	if (((fsize%size) == 0)&& fsize<(32*max_number))
		number = fsize /size;
	else
		return NULL;

        print_cubeaudit("read hashlist app num is %d max %d\n",number,max_number);
	
	policys=malloc(sizeof(*policy_buffer));
	if(policys==NULL)
		return NULL;
	policys->digest_num=number;
	policys->digest_size=size;
	policys->digest_array=malloc(number*size);
	if(policys->digest_size==NULL)
	{
		free(policys);
		return NULL;
	}	
	
	memset(policys->digest_array,0,policys->digest_num*policys->digest_size);

	for (i=0;i<number;i++){
		ret=read(fd,policys->digest_array+size*i,size);
			
		if(ret!=size) {
			print_cubeerr("read [%s] error!\n",filename);
			free(policys->digest_array);
			free(policys);
			return NULL;
		}
	}
	return policys;
}

struct policy_buffer * policy_add_package(struct policy_buffer * base_policy,struct policy_buffer * package_policy)
{
   int max_no; 
   int curr_site;
   int i,j;
   struct policy_buffer * merge_policy;
   merge_policy=malloc(sizeof(*policy_buffer));
   if(merge_policy ==NULL)
        return NULL;
   if(base_policy->digest_size!=package_policy->digest_size)
        return NULL;
   merge_policy->digest_size=base_policy->digest_size;
   max_no=base_policy->digest_num+package_policy->digest_num;
   merge_policy->digest_array=malloc(merge_policy->digest_size*max_no);
   if(merge_policy==NULL)
   {
        free(merge_policy);
        return NULL;
   }
   merge_policy->digest_num=0;
   
   i=0;
   j=0;
   curr_site=0;

   do
   {
        int comp_value;
        if(i==base_policy->digest_num)
            break;
        if(j==package_policy->digest_num)
            break;
        comp_value = compare_hash(base_policy->digest_array+i*merge_policy->digest_size,package_policy->digest_array+j*merge_policy->digest_size);
        if(comp_value>0)
        {
            Memcpy(merge_policy->digest_array+curr_site*merge_policy->digest_size,package_policy->digest_array+j*merge_policy->digest_size,merge_policy->digest_size);
            j++;
            curr_site++;
        }
        else if(comp_value<0)
        {
            Memcpy(merge_policy->digest_array+curr_site*merge_policy->digest_size,base_policy->digest_array+i*merge_policy->digest_size,merge_policy->digest_size);
            i++;
            curr_site++;
        }
        else
        {
            j++;
        }
   }while(1);
   if(i<base_policy->digest_num)
   {
        j=base_policy->digest_num-i;
        Memcpy(merge_policy->digest_array+curr_site*merge_policy->digest_size,base_policy->digest_array+i*merge_policy->digest_size,j*merge_policy->digest_size);
        curr_site+=j;
   }
   else if(j<base_policy->digest_num)
   {
        i=base_policy->digest_num-j;
        Memcpy(merge_policy->digest_array+curr_site*merge_policy->digest_size,package_policy->digest_array+j*merge_policy->digest_size,i*merge_policy->digest_size);
        curr_site+=j;
   }

   if(curr_site<max_no)
   {
        BYTE * temp_buffer=malloc(merge_policy->digest_size * curr_site);
        Memcpy(temp_buffer,merge_policy->digest_array,curr_site*merge_policy->digest_size);
        free(merge_policy->digest_array);
        merge_policy->digest_array=temp_buffer;
   }

   merge_policy->digest_num=curr_site;  

   if(base_policy->digest_num!=0)
        free(base_policy->digest_array);
   if(package_policy->digest_num!=0)
        free(package_policy->digest_array);
        
   free(base_policy);
   free(package_policy);



   return merge_policy;
}

struct policy_buffer * policy_del_package(struct policy_buffer * base_policy,struct policy_buffer * package_policy)
{
   int max_no; 
   int curr_site;
   int span;
   int i,j;
   if(base_policy->digest_size!=package_policy->digest_size)
        return NULL;
   
   i=0;
   j=0;
   curr_site=0;
   span=0;

   do
   {
        int comp_value;
        if(i==base_policy->digest_num)
            break;
        if(j==package_policy->digest_num)
        {
            if(span>0)   
                // copy left digest
                Memcpy(base_policy->digest_array+(i-span)*base_policy->digest_size,
                    base_policy->digest_array+i*base_policy->digest_size,
                    base_policy->digest_size*(base_policy->digest_num-i));
            break;
        }
        comp_value = compare_hash(base_policy->digest_array+i*base_policy->digest_size,package_policy->digest_array+j*base_policy->digest_size);
        if(comp_value>0)
        {
            j++;
        }
        else if(comp_value<0)
        {
            if(span>0)
                Memcpy(base_policy->digest_array+(i-span)*base_policy->digest_size,base_policy->digest_array+i*base_policy->digest_size,base_policy->digest_size);
            i++;
        }
        else
        {
            i++;
            span++;   
            j++;
        }
   }while(1);

    
   base_policy->digest_num-=span;

   if(span>0)
   {
        BYTE * temp_buffer=malloc(base_policy->digest_size *base_policy->digest_num);
        Memcpy(temp_buffer,base_policy->digest_array,base_policy->digest_num*base_policy->digest_size);
        free(base_policy->digest_array);
        base_policy->digest_array=temp_buffer;
   }

   if(package_policy->digest_num!=0)
        free(package_policy->digest_array);
   free(package_policy);

   return base_policy;
}

void * appbsearch(const void *key, const void *base, size_t nmemb, size_t size,
		int (*compar)(const void *, const void *))
{
	const void *entry;
	unsigned int l;
	unsigned int u;
	unsigned int m;
    int comp_value;

	BYTE * testbuf=key;

	l = -1;
	u = nmemb;
	while (l + 1 != u) {
		m = (l + u) / 2;
		entry = base + m * size;
        comp_value=compar(key,entry);
		if (comp_value > 0)
			l = m;
		else if(comp_value ==0)
            return ((void *)entry);
        else    
			u = m;
	}

	entry = base + u * size;
	//printk("num:%d,dig_size:%d\n",nmemb,size);
	if (u == nmemb
			|| compar(key, entry) != 0)
		return (NULL);

	return ((void *)entry);
}
